#include "OLED12864.h"
#include "ascii_font.h" //字库

static OLED_MemoryModeEnum mem_mode = OLED_MemoryMode_PAGE_ADDR; // 静态局部变量，保存OLED的地址模式的

void OLED_WriteCmd(uint8_t cmd)
{
    SimulateI2C_Start();
    SimulateI2C_SendByte(OLED_ADDRESS);
    SimulateI2C_ReceiveAck();
    SimulateI2C_SendByte(0x00);
    SimulateI2C_ReceiveAck();
    SimulateI2C_SendByte(cmd);
    SimulateI2C_ReceiveAck();
    SimulateI2C_Stop();
}
void OLED_WriteData(uint8_t dat)
{
    SimulateI2C_Start();
    SimulateI2C_SendByte(OLED_ADDRESS);
    SimulateI2C_ReceiveAck();
    SimulateI2C_SendByte(0x40);
    SimulateI2C_ReceiveAck();
    SimulateI2C_SendByte(dat);
    SimulateI2C_ReceiveAck();
    SimulateI2C_Stop();
}
void OLED_WriteDataBuffer(uint8_t *pBuf, uint16_t Size)
{
    SimulateI2C_Start();
    SimulateI2C_SendByte(OLED_ADDRESS);
    SimulateI2C_ReceiveAck();
    SimulateI2C_SendByte(0x40);
    SimulateI2C_ReceiveAck();
    while (Size--)
    {
        SimulateI2C_SendByte(*pBuf);
        pBuf++;
        SimulateI2C_ReceiveAck();
    }
    SimulateI2C_Stop();
}

void OLED_Display_ON(void)
{
    OLED_WriteCmd(0xAF);
}
void OLED_Display_OFF(void)
{
    OLED_WriteCmd(0xAE);
}

void OLED_SEG_REMAP(void)
{
    OLED_WriteCmd(0xA1);
}
void OLED_SEG_NOREMAP(void)
{
    OLED_WriteCmd(0xA0);
}

void OLED_SCAN_NORMAL(void)
{
    OLED_WriteCmd(0xC0);
}
void OLED_SCAN_REMAP(void)
{
    OLED_WriteCmd(0xC8);
}

/**
 * @note (黑底白字)
 */
void OLED_Display_NORMAL(void)
{
    OLED_WriteCmd(0xA6);
}

/**
 * @note (白底黑字)
 */
void OLED_Display_INVERSE(void)
{
    OLED_WriteCmd(0xA7);
}

void OLED_Init(void)
{
    SimulateI2C_Config();
    OLED_Display_OFF();
    OLED_SetMemoryMode(OLED_MemoryMode_PAGE_ADDR);                     // 0. 设置地址模式
    OLED_SetMuxRatio(0x3F);                                            // 1. 设置多路复用率
    OLED_SetOffset(0x00);                                              // 2. 设置显示的偏移值
    OLED_SetStartLine(0x00);                                           // 3. 设置起始行
    OLED_SEG_REMAP();                                                  // 4. 行翻转
    OLED_SCAN_REMAP();                                                 // 5. 扫描
    OLED_SetComPins(OLED_ComPinsMode_SEQ, OLED_ComReMapState_NOREMAP); // 6. COM 引脚设置
    OLED_SetContrastValue(0x7F);                                       // 7. 设置对比度
    OLED_Display_INVERSE();                                            // 9. 显示模式
    OLED_SetDCLK_Freq(0x00, 0x08);                                     // 10. 设置分频系数和频率增值
    OLED_SetChargePump(OLED_ChargePumpState_ENABLE);                   // 11. 使能电荷碰撞
    OLED_SetComPins(OLED_ComPinsMode_ALT, OLED_ComReMapState_NOREMAP);
    OLED_Display_ON();
}

/*
 *  @brief  设置OLED的对比度
 *  @param value [0~255]
 */
void OLED_SetContrastValue(uint8_t value)
{
    OLED_WriteCmd(0x81);
    OLED_WriteCmd(value);
}

/*
 *  @brief 设置OLED在页地址模式下的显示起始行地址
 *  @param addr : 起始列地址
 */
void OLED_SetPageColAddr(uint8_t addr)
{
    if (mem_mode != OLED_MemoryMode_PAGE_ADDR)
        return;
    if (addr > 0x7F)
        return;
    OLED_WriteCmd(0x00 + (addr & 0x0F));
    OLED_WriteCmd(0x10 + (addr >> 4));
}

/*
 * @brief 设置OLED的地址模式
 */
void OLED_SetMemoryMode(OLED_MemoryModeEnum mode)
{
    if ((mode != OLED_MemoryMode_H_ADDR) && (mode != OLED_MemoryMode_V_ADDR) && (mode != OLED_MemoryMode_PAGE_ADDR))
        return;
    OLED_WriteCmd(0x20);
    OLED_WriteCmd(mode);
    mem_mode = mode;
}

/*
 *  @brief 设置OLED在页地址模式下的显示起始页地址
 *  @param : 起始页地址[0~7]
 */
void OLED_SetPageAddr(uint8_t addr)
{
    if (mem_mode != OLED_MemoryMode_PAGE_ADDR)
        return;
    if (addr > 7)
        return;
    OLED_WriteCmd(0xB0 + addr);
}

/**
 *
 * @brief  设置OLED从第line行开始显示
 * @param line[0~63]
 */
void OLED_SetStartLine(uint8_t line)
{
    if (line > 63)
        return;
    OLED_WriteCmd(0x40 + line);
}

/*
 *  函数名：OLED_SetMuxRatio
 *  功能描述：设置OLED复用率
 *  输入参数：ratio-->复用率值
 */
void OLED_SetMuxRatio(uint8_t ratio)
{
    if ((ratio < 15) || (ratio > 63))
        return;
    OLED_WriteCmd(0xA8);
    OLED_WriteCmd(ratio);
}

/*
 *  功能描述：设置OLED的COM引脚偏移值
 *  输入参数：offset-->COM偏移值
 */
void OLED_SetOffset(uint8_t offset)
{
    if (offset > 63)
        return;
    OLED_WriteCmd(0xD3);
    OLED_WriteCmd(offset);
}

/**
 *  功能描述：设置OLED的扫描周期和晶振频率
 *  输入参数：div-->时钟分频系数
 *           freq-->晶振频率
 */
void OLED_SetDCLK_Freq(uint8_t div, uint8_t freq)
{
    if ((div > 0x0F) || (freq > 0x0F))
        return;
    OLED_WriteCmd(0xD5);
    OLED_WriteCmd(div + (freq << 4));
}

/**
 * @brief 设置OLED的COM引脚属性
 * @param  OLED_ComPinsModeEnum mode : COM引脚模式是连续的还是可选择的
 * @param  OLED_ComReMapStateEnum state : COM引脚翻转与否
 */
void OLED_SetComPins(OLED_ComPinsModeEnum mode, OLED_ComReMapStateEnum state)
{
    if ((mode != OLED_ComPinsMode_SEQ) && (mode != OLED_ComPinsMode_ALT))
        return;
    if ((state != OLED_ComReMapState_NOREMAP) && (state != OLED_ComReMapState_REMAP))
        return;
    OLED_WriteCmd(0xDA);
    OLED_WriteCmd(0x02 + (mode << 4) + (state << 5));
}

/*
 *  函数名：OLED_SetChargePump
 *  功能描述：打开或关闭OLED的电荷泵
 *  输入参数：state-->电荷泵打开或关闭
 */
void OLED_SetChargePump(OLED_ChargePumpStateEnum state)
{
    if ((state != OLED_ChargePumpState_DISABLE) && (state != OLED_ChargePumpState_ENABLE))
        return;
    OLED_WriteCmd(0x8D);
    OLED_WriteCmd((state << 2) + 0x10);
}

/**
 * @brief 设置像素显示的起始页和起始列地址
 * @param    page-->页地址模式下的起始页地址
 * @param    col-->页地址模式下的起始列地址
 */
void OLED_SetPosition(uint8_t page, uint8_t col)
{
    OLED_SetPageAddr(page);
    OLED_SetPageColAddr(col);
}

/**
 * @brief CLS 8X16显示用到2page
 *  逐字符清屏
 */
void OLED_Clear(void)
{
    uint8_t p = 0; // page
    uint8_t col = 0;
    uint8_t buf[8] = {0};
    while (p < 8)
    {
        for (col = 0; col < 128; col += 8)
        {
            // 字符上半部分
            OLED_SetPosition(p, col);
            OLED_WriteDataBuffer(&buf[0], 8);
            // 字符下半部分
            OLED_SetPosition(p + 1, col);
            OLED_WriteDataBuffer(&buf[0], 8);
        }
        p += 2;
    }
}

/**
 * @brief 8X16显示用到2page
 * @param page 起始页地址[0-6], 从7开始不足以显示2Page
 * @param col  起始列地址[0-120], >120不能完整显示8位宽度
 * @param c '字符'或者是对应assii码
 */
void OLED_PutChar8X16(uint8_t page, uint8_t col, char c)
{
    // 字符上半部分
    OLED_SetPosition(page, col);
    OLED_WriteDataBuffer((uint8_t *)&ascii_font[c][0], 8);
    // 字符下半部分
    OLED_SetPosition(page + 1, col);
    OLED_WriteDataBuffer((uint8_t *)&ascii_font[c][8], 8);
}

/**
 * @brief 显示16*16(中文)
 */
void OLED_PutZH16X16(uint8_t page, uint8_t col, uint8_t index)
{
    // 上半部分
    OLED_SetPosition(page, col);
    OLED_WriteDataBuffer((uint8_t *)&zh_font[index][0], 16);
    // 下半部分
    OLED_SetPosition(page + 1, col);
    OLED_WriteDataBuffer((uint8_t *)&zh_font[index][16], 16);
}

/**
 * @brief 8X16显示:一行Str用到2page,最多16个字符。
 */
void OLED_PrintStr8X16(uint8_t page, uint8_t col, char *str)
{
    while (*str)
    {
        OLED_PutChar8X16(page, col, *str);
        col += 8;
        if (col > 127)
        {
            // 一行Str用到2page
            page += 2;
        }
        if (page > 7)
        {
            page = 0;
        }
        str++;
    }
}
